package drds.server.net.packet;

import java.nio.ByteBuffer;

import drds.server.net.SessionContext$LifeCycle;

/**
 * From server to client in response to command, if error.
 * 
 * <pre>
 * Bytes                       Name
 * -----                       ----
 * 1                           field_count, always = 0xff
 * 2                           errno
 * 1                           (sqlstate marker), always '#'
 * 5                           sqlstate (5 characters)
 * n                           message
 * 
 * @see http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Error_Packet
 * </pre>
 * 
 * @author 曹正辉<330937205@qq.com>
 * 
 */
public class ErrorPacket extends Packet {
	public static final byte FIELD_COUNT = (byte) 0xff;
	private static final byte SQLSTATE_MARKER = (byte) '#';
	private static final byte[] DEFAULT_SQLSTATE = "HY000".getBytes();

	public int errorNo;
	public byte[] sqlState = DEFAULT_SQLSTATE;
	public byte[] errorMessage;

	public void read(BinaryPacket binaryPacket) {
		packetLength = binaryPacket.packetLength;
		packetId = binaryPacket.packetId;
		BytesHolder bytesHolder = new BytesHolder(binaryPacket.bytes);
		errorNo = bytesHolder.readInt2();
		if (bytesHolder.hasRemaining() && (bytesHolder.read(bytesHolder.position()) == SQLSTATE_MARKER)) {
			bytesHolder.read();
			sqlState = bytesHolder.readBytes(5);
		}
		errorMessage = bytesHolder.readBytes();
	}

	public void read(byte[] bytes) {
		BytesHolder bytesHolder = new BytesHolder(bytes);
		packetLength = bytesHolder.readInt3();
		packetId = bytesHolder.read();
		errorNo = bytesHolder.readInt2();
		if (bytesHolder.hasRemaining() && (bytesHolder.read(bytesHolder.position()) == SQLSTATE_MARKER)) {
			bytesHolder.read();
			sqlState = bytesHolder.readBytes(5);
		}
		errorMessage = bytesHolder.readBytes();
	}

	public byte[] writeToBytes(SessionContext$LifeCycle sessionContext) {
		ByteBuffer byteBuffer = sessionContext.allocate();
		byteBuffer = write(byteBuffer, sessionContext, false);
		byteBuffer.flip();
		byte[] bytes = new byte[byteBuffer.limit()];
		byteBuffer.get(bytes);
		sessionContext.recycle(byteBuffer);
		return bytes;
	}

	public byte[] writeToBytes() {
		ByteBuffer byteBuffer = ByteBuffer.allocate(computePacketSize() + 4);
		int size = computePacketSize();
		ByteBufferUtil.writeInt3(byteBuffer, size);
		byteBuffer.put(packetId);
		ByteBufferUtil.writeInt2(byteBuffer, errorNo);
		byteBuffer.put(sqlState);
		if (errorMessage != null) {
			byteBuffer.put(errorMessage);
		}
		byteBuffer.flip();
		byte[] bytes = new byte[byteBuffer.limit()];
		byteBuffer.get(bytes);

		return bytes;
	}

	@Override
	public ByteBuffer write(ByteBuffer byteBuffer, SessionContext$LifeCycle sessionContext, boolean writeSocketIfFull) {
		int size = computePacketSize();
		byteBuffer = sessionContext.checkWriteByteBuffer(byteBuffer, sessionContext.getPacketHeaderSize() + size, writeSocketIfFull);
		ByteBufferUtil.writeInt3(byteBuffer, size);
		byteBuffer.put(packetId);
		ByteBufferUtil.writeInt2(byteBuffer, errorNo);
		byteBuffer.put(sqlState);
		if (errorMessage != null) {
			byteBuffer = sessionContext.writeToByteBuffer(errorMessage, byteBuffer);
		}
		return byteBuffer;
	}

	public void write(SessionContext$LifeCycle sessionContext) {
		ByteBuffer byteBuffer = sessionContext.allocate();
		byteBuffer = this.write(byteBuffer, sessionContext, true);
		sessionContext.write(byteBuffer);
	}

	@Override
	public int computePacketSize() {
		int size = 9;// 1 + 2 + 1 + 5
		if (errorMessage != null) {
			size += errorMessage.length;
		}
		return size;
	}

}